﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Archiver
{
    internal class Archiving
    {
        private const int maxS = 128;
        private const int minS = 32;

        private string FileNameIn;
        private string FileNameOut;

        private Stream FileOut;

        private string FileBufToWrite;

        public Archiving(string FileNameIn, string FileNameOut)
        {
            this.FileNameIn = FileNameIn;
            this.FileNameOut = FileNameOut;

            FileOut = File.Open(this.FileNameOut, FileMode.Create);
            FileBufToWrite = "";
        }

        private void StartSaveToFile()
        {
            while (FileBufToWrite.Length>=8)
            {
                var t = FileBufToWrite.Substring(0, 8);
                //FileOut.WriteByte(Binary.ConvertBinToByte(t));
                FileOut.WriteByte(Convert.ToByte(t,2));
                FileBufToWrite = FileBufToWrite.Remove(0, 8); 
            }
        }

        private void SaveToFile(int f, int s, int x, int y, int n)
        {
            FileBufToWrite += Binary.ConvertIntToBin(f, 8);
            FileBufToWrite += Binary.ConvertIntToBin(s, 8);
            FileBufToWrite += Binary.ConvertIntToBin(x, 8);
            FileBufToWrite += Binary.ConvertIntToBin(y, 8);
            FileBufToWrite += Binary.ConvertIntToBin(n, 8);
            StartSaveToFile(); 
        }

        private void SaveToFile(int f, int s, List<bool> x)
        {
            FileBufToWrite += Binary.ConvertIntToBin(f, 8);
            FileBufToWrite += Binary.ConvertIntToBin(s, 8);
            FileBufToWrite += Binary.BitsArryToStr(x);
            StartSaveToFile();
        }

        private void SaveToFile(List<bool> x)
        {
            FileBufToWrite += Binary.BitsArryToStr(x);
            StartSaveToFile();
        }
        
        private void EndSaveToFile()
        {
            StartSaveToFile();
            if (FileBufToWrite.Length != 0)
                FileOut.WriteByte(Convert.ToByte(FileBufToWrite, 2));
            
            FileOut.Close();
        }

        private void GetBitsByNextN(ref List<bool> bits)
        {
            var b = bits[0];
            bits.RemoveAt(0);
            bits.Add(b);
        }
        
        private void GetBitsByBackN(ref List<bool> bits)
        {
            var b = bits[bits.Count - 1];
            bits.RemoveAt(bits.Count - 1);
            var t = new List<bool>(); 
            t.Add(b);
            t.AddRange(bits);
            bits = t;
        }

        private bool SearchXY(List<bool> BitValue, out int x,out int y,out int n)
        {
            var value = new List<bool>(BitValue);
            int x1, y1;
            var n1 = 0;
            do
            {
                if (FunctionTableValues.IsValuePresent(Binary.TrimStart(value), out x1, out y1))
                {
                    x = x1;
                    y = y1;
                    n = n1;
                    return true;
                }
                GetBitsByNextN(ref value);
                n1++;
            } while (n1 < BitValue.Count);
          
            x = 0;
            y = 0;
            n = 0;
            return false;
        }

        public void StartArchiving()
        {
            var j = 0;
            var BinData = new FileReader(FileNameIn);
            var time = DateTime.Now;
            while (true)
            {
                var s = maxS;
                var nu = BinData.GetBits(s);
                s = nu.Count;
                if (nu.Count == 0)
                    break;
                while (nu.Count > minS)
                {
                    int x, y, n;
                    
                    if (SearchXY(nu, out x,out y, out n))
                    {
                        SaveToFile(1, nu.Count, x, y, n);
                        j++;
                        break;
                    }
                    s--;
                    nu.RemoveAt(nu.Count-1); 
                }
                
                if (nu.Count<=minS)
                    SaveToFile(0, nu.Count, nu);

                BinData.DeleteBits(nu.Count);
                Console.WriteLine(BinData.Length);
            }
            EndSaveToFile();
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Произведено замен: {0}",j);
            var tt = DateTime.Now - time;
            Console.WriteLine("время  " + tt.Seconds + "," + tt.Milliseconds);
        }

        public void StartDeArchiving()
        {
            var BinData = new FileReader(FileNameIn);

            while (BinData.Length>0)
            {
                byte f, s, x, y, n;

                f = Binary.ConvertBinToByte(BinData.GetBits(8));
                BinData.DeleteBits(8);
                s = Binary.ConvertBinToByte(BinData.GetBits(8));
                BinData.DeleteBits(8);
                if (f == 0)
                {
                    SaveToFile(BinData.GetBits(s));
                    BinData.DeleteBits(s);
                    continue;
                }

                x = Binary.ConvertBinToByte(BinData.GetBits(8));
                BinData.DeleteBits(8);
                y = Binary.ConvertBinToByte(BinData.GetBits(8));
                BinData.DeleteBits(8);
                n = Binary.ConvertBinToByte(BinData.GetBits(8));
                BinData.DeleteBits(8);

                var val = FunctionTableValues.GetValueByXY(x, y);
                if (val.Count < s)
                {
                    var t = new List<bool>();
                    var k = s - val.Count;
                    while (k > 0)
                    {
                        t.Add(false);
                        k--;
                    }
                    t.AddRange(val);
                    val = t;
                }

                for (var i = 0; i < n; i++)
                    GetBitsByBackN(ref val);
                SaveToFile(val);
            }
            EndSaveToFile();
        }

        public void testsqn()
        {
            var n1 = 1;
            var value = new List<bool>();
                value.Add(true); 
                value.Add(false);
                value.Add(false);
                value.Add(false);
                value.Add(true);
                value.Add(true);
                value.Add(true);
                value.Add(true);

            for (var i = 0; i < n1; i++)
                GetBitsByBackN(ref value);

            var t = value;
        }
    }
}